package com.ayoldi.podcastusbsync;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.JAXBException;

import org.apache.commons.daemon.Daemon;
import org.apache.commons.daemon.DaemonContext;
import org.apache.commons.daemon.DaemonInitException;
import org.apache.log4j.Logger;

import com.ayoldi.podcastusbsync.configurator.ConfigurationManager;
import com.ayoldi.podcastusbsync.configurator.ConfigurationUtils;
import com.ayoldi.podcastusbsync.downloader.FeedsDownloaderThread;
import com.ayoldi.podcastusbsync.model.Configuration;
import com.ayoldi.podcastusbsync.model.Configuration.UsbDeviceConfiguration;
import com.ayoldi.podcastusbsync.synchronizer.USBDeviceCheckerThread;
import com.ayoldi.podcastusbsync.synchronizer.USBDeviceSynchronizer;

public class PodcastUSBSync implements Daemon{

	static Logger LOG = Logger.getLogger(PodcastUSBSync.class);
	
	FeedsDownloaderThread feedsDownloader;
	List<USBDeviceCheckerThread> usbDeviceCheckerThreads;
	
	private static PodcastUSBSync podcastUSBSyncInstance = new PodcastUSBSync();
	
	public void startFeedsDownloader(Configuration config, File file, ConfigurationManager configManager){
		FeedsDownloaderThread feedsDownloaderThread=new FeedsDownloaderThread(config,file,configManager);
		feedsDownloaderThread.start();
		this.feedsDownloader=feedsDownloaderThread;
	}
	
	public void startDevicesSynchronizer(Configuration config){
		long pollingUSBInterval=50000;
		try{
			pollingUSBInterval=Long.parseLong(ConfigurationUtils.getGlobalOptions(config).getUSBMonitorPeriod())*1000;
		}
		catch (Exception e){
			e.printStackTrace();
			LOG.warn("Could not parse usb monitor period: "+ConfigurationUtils.getGlobalOptions(config).getUSBMonitorPeriod());
		}		
		
		List<UsbDeviceConfiguration> devicesConfig= ConfigurationUtils.getUsbDeviceConfigurations(config);
		usbDeviceCheckerThreads=new ArrayList<USBDeviceCheckerThread>();
		for (UsbDeviceConfiguration deviceConfig : devicesConfig){
			USBDeviceSynchronizer synchronizer=new USBDeviceSynchronizer(config,deviceConfig);
			USBDeviceCheckerThread thread= new USBDeviceCheckerThread(synchronizer, pollingUSBInterval);
			thread.start();
			usbDeviceCheckerThreads.add(thread);
		}
	}
	
	public static void main (String args[]){
		
		File file = new File("configuration.xml");

		ConfigurationManager configManager = new ConfigurationManager();
		
		Configuration config=null;
		config=configManager.readConfig(file);
		
		if (config==null){
			return;
		}
		
		podcastUSBSyncInstance.startFeedsDownloader(config, file, configManager);		
		podcastUSBSyncInstance.startDevicesSynchronizer(config);
		
		
		LOG.info("PodcastUSBSync initialized succesfully!");		
		
		
	}

	/**
     * Static methods called by prunsrv to start/stop
     * the Windows service.  Pass the argument "start"
     * to start the service, and pass "stop" to
     * stop the service.
     *
     * Taken lock, stock and barrel from Christopher Pierce's blog at http://blog.platinumsolutions.com/node/234
     *
     * @param args Arguments from prunsrv command line
     **/
    public static void windowsService(String args[]) {
        String cmd = "start";
        if (args.length > 0) {
            cmd = args[0];
        }

        if ("start".equals(cmd)) {
        	podcastUSBSyncInstance.windowsStart();
        }
        else {
        	podcastUSBSyncInstance.windowsStop();
        }
    }

    public void windowsStart() {
    	LOG.debug("windowsStart called");
    	initialize();
    }

    public void windowsStop() {
    	LOG.debug("windowsStop called");
        terminate();
        synchronized(this) {
            // stop the start loop
            this.notify();
        }
    }

    /**
     * Do the work of starting the engine
     */
    private void initialize() {
    	main(null);
    }
    
    /**
     * Cleanly stop the engine.
     */
    public void terminate() {
    	feedsDownloader.setStopped(true);
    	for (USBDeviceCheckerThread checkerThread : usbDeviceCheckerThreads){
    		checkerThread.setStopped(true);
    	}
    	while (feedsDownloader.isAlive()){
    		synchronized(this) {
                try {
                    this.wait(5000);  // wait 5s and check if stopped
                }
                catch(InterruptedException ie){}
            }
    	}
    	boolean allCheckersDead=false;
    	while (!allCheckersDead){
    		allCheckersDead=true;
    		for (USBDeviceCheckerThread checkerThread : usbDeviceCheckerThreads){
        		if (checkerThread.isAlive()){
        			allCheckersDead=false;
        			continue;
        		}
        	}
    		synchronized(this) {
                try {
                    this.wait(5000);  // wait 5s and check if stopped
                }
                catch(InterruptedException ie){}
            }
    	}
    }
    
    // Implementing the Daemon interface is not required for Windows but is for Linux
    public void init(DaemonContext arg0) throws Exception {
    	LOG.debug("Daemon init");
    }

    public void start() {
    	LOG.debug("Daemon start");
    	initialize();
    }

    public void stop() {
    	LOG.debug("Daemon stop");
        terminate();
    }

    public void destroy() {
    	LOG.debug("Daemon destroy");
    }

    
	
}
